import Main from "./main.js"
import { arrayToHexString, decodeWeight, buildCmd, isHeartRateScale, getCmdMac } from "./utils.js"
const BodyfatReport = require('../report/Bodyfat')
const BmiReport = require('../report/BMI')
const moment = require('../libs/moment')
const SECOND_2000_YEAR = 946656000;
const { appType } = require('../theme.config.js')
const broadcastTag = "QN-S3"
const $log = require('../utils/log.js')
const { macReg } = require('../utils/anomaly.js')
const oneToOneTag = 'QS1'

import {
  getUUID,
  UUID_WSP_181D,
  UUID_WSP_1805,
  UUID_WSP_READ,
  UUID_WSP_WRITE,
  UUID_WSP_FFE1,
  UUID_WSP_FFE2,
  UUID_WSP_2A2B,
  UUID_WSP_2A9E,
  UUID_WSP_2A9D,
  UUID_WSP_181C,
  UUID_WSP_2A9F,
  UUID_WSP_181B,
  UUID_WSP_2A9B,
  UUID_WSP_2A8C,
  UUID_WSP_2A8E,
  UUID_WSP_2A85,
  UUID_WSP_2A80,
  UUID_WSP_2AFF,
  UUID_WSP_2A9C,
  wspTime,
  isWsp,
} from '../utils/wsp.js'


class Scale extends Main {
  
  that = null
  isScale1 = false
  weightScale = 100
  cmdString = null
  lastWeightTime = 0
  deviceObj = {}
  device = null
  scaleData = []
  manufacturer = ""
  deviceInfo = {}
  cmdTimer = null
  stableDataStatus = false//是否数据稳定状态
  scaleType = ''
  mac = null
  
  nowUUID = ''

  constructor(obj = {}) {
    let params = {
      type: "scale",
      name: ["QN-Scale", "QN-Scale1", "ZT-Scale1", broadcastTag],
      //services: ["FFE0"],
      serviceId: "0000FFE0-0000-1000-8000-00805F9B34FB",
      readCharacteristicId: "0000FFE1-0000-1000-8000-00805F9B34FB",
      writeCharacteristicId: "0000FFE3-0000-1000-8000-00805F9B34FB",
      serviceId1: "0000FFF0-0000-1000-8000-00805F9B34FB",
      readCharacteristicId1: "0000FFF1-0000-1000-8000-00805F9B34FB",
      writeCharacteristicId1: "0000FFF2-0000-1000-8000-00805F9B34FB"
    }
    Object.assign(obj, params)

    super(obj)

    this.that = obj

    this.onValueChangePush(this.onValueChange.bind(this))

    this.onDeviceScanObjPush(params.type, this.onDeviceScanCallback.bind(this))

    this.arrayToHexString = arrayToHexString
    this.decodeWeight = decodeWeight

    if (this.that.log) {
      this.log = this.that.log({ level: 1 })
    } else {
      this.log = {
        debug() {}
      }
    }
    console.log('wx.getLogManager:', this.log)
  }

  getMac(advertisArrayData, advertisServiceUUIDs) {
    let startNum = 7
    let endNum = 13
    let mac_temp = []

    if (isWsp(advertisArrayData, advertisServiceUUIDs)) {//wsp
      startNum = 11
      endNum = 17
    }

    for (let i = startNum; i < endNum; i++) {
      let item = advertisArrayData[i]
      let mac_str = item.toString(16).toLocaleUpperCase()
      if (mac_str.length === 1) {
        mac_str = `0${mac_str}`
      }
      mac_temp.push(mac_str)
    }
    
    return mac_temp.reverse().join(":")
  }

  getMacBroadcastScale(advertisArrayData) {
    let startNum = 2
    let endNum = 8
    let mac_temp = []

    for (let i = startNum; i < endNum; i++) {
      let item = advertisArrayData[i]
      let mac_str = item.toString(16).toLocaleUpperCase()
      if (mac_str.length === 1) {
        mac_str = `0${mac_str}`
      }
      mac_temp.push(mac_str)
    }

    return mac_temp.join(":")
  }

  getMacOneToOne(advertisArrayData) {
    let mac = advertisArrayData.slice(4, 10)
    let tmp = []

    mac.forEach(item => {
      let str = item.toString(16).toUpperCase()
      if (str.length == 1) {
        str = '0' + str
      }
      tmp.push(str)
    })
    return tmp.join(':')
  }

  onDeviceScanCallback(device) {
    const { advertisData, advertisServiceUUIDs } = device
    const advertisArrayData = new Uint8Array(advertisData)
    const advertisDataString = arrayToHexString(advertisArrayData)
    let localName = device.name || device.localName
    let mac = ""

    if (localName == oneToOneTag) {
      mac = this.getMacOneToOne(advertisArrayData)
    } else if (localName == broadcastTag) {
      mac = this.getMacBroadcastScale(advertisArrayData)
    } else {
      mac = this.getMac(advertisArrayData, advertisServiceUUIDs)
    }

    this.mac = mac

    this.log.debug('mac', this.mac)
    this.log.debug('device', device)
    this.log.debug('advertisArrayData', advertisArrayData)
    this.log.debug('advertisDataString', advertisDataString)
    $log.writeHead('mac', this.mac)
    $log.writeHead('device', device)
    $log.writeHead('advertisArrayData', advertisArrayData)
    $log.writeHead('advertisDataString', advertisDataString)
    //if (mac != "A4:C1:38:CA:CE:C2" && mac != "E1:CF:31:5D:B6:93") return
    //if (mac != "A4:C1:38:CA:CE:C2") return
    //if (mac != "66:AA:55:00:00:21") return
    //if (mac != "66:AA:55:FF:FF:4A") return
    //if (mac != "F7:53:C8:C5:2B:86") return
    // if (mac != "3C:71:BF:E5:3A:6E") return
    
    let deviceInfo = {}
    
    device.mac = mac
    deviceInfo.name = localName
    deviceInfo.localName = localName
    deviceInfo.mac = mac
    //deviceInfo.manufacturer = advertisDataString
    deviceInfo.internal_model = advertisDataString.substring(4, 8)
    //deviceInfo.device = device
    deviceInfo.rssi = device.RSSI
    deviceInfo.deviceId = device.deviceId
    deviceInfo.advertisServiceUUIDs = device.advertisServiceUUIDs
    deviceInfo.advertisArrayData = device.advertisArrayData

    this.deviceObj[mac] = deviceInfo
    this.manufacturer = deviceInfo.internal_model
    
    if (localName == oneToOneTag) {
      if (this.device && mac != this.device.mac) {
        return
      }

      deviceInfo.manufacturer = advertisDataString
      deviceInfo.advertisArrayData = advertisArrayData

      this.deviceInfo = deviceInfo
      this.device = deviceInfo
      this.that.onBroadcastScale && this.that.onOneToOne(deviceInfo)
    } else if (localName == broadcastTag) {
      if (this.device && mac != this.device.mac) {
        return
      }
      
      deviceInfo.manufacturer = advertisDataString
      deviceInfo.advertisArrayData = advertisArrayData

      this.deviceInfo = deviceInfo
      this.device = deviceInfo
      this.that.onBroadcastScale && this.that.onBroadcastScale(deviceInfo)

    } else {
      if (this.device) return
      
      this.deviceInfo = deviceInfo
      
      if (this.that.auto_connect) {
        device.advertisArrayData = advertisArrayData
        this.device = device
        this.stopDiscovery()
        this.that.onDeviceScan && this.that.onDeviceScan([this.deviceObj[mac]])
      } else {
        let tempArr = []

        for (let key in this.deviceObj) {
          tempArr.push(this.deviceObj[key])
        }

        this.that.onDeviceScan && this.that.onDeviceScan(tempArr)
      }
    }
    this.log.debug('deviceInfo', this.deviceInfo)
    $log.writeHead('deviceInfo', this.deviceInfo)
  }

  onValueChange({ deviceId, value }) {
    let data = new Uint8Array(value)
    const scaleType = data[2];
    const cmd = arrayToHexString(data)
    console.log('收到数据', cmd)
    this.log.debug('收到数据', cmd)
    $log.writeBody('收到数据', cmd)
    this.scaleData = this.formatCmdData(cmd)
    this.scaleType = scaleType

    switch (data[0]) {
      case 0x12:
        {
          this.stableDataStatus = false
          if (this.ishearRate(scaleType, data[10])) {
            //this.heartRate = (data[10] & 0x01) === 1 ? true : false
            this.heartRate = true
            console.log('心率秤')
          } else {
            this.heartRate = false
          }
          this.cmdString = cmd

          this.weightScale = (data[10] & 0x01) === 1 ? 100 : 10
          this.lastWeightTime = 0

          if (!macReg(this.mac)) {
            this.mac = getCmdMac(cmd)

            this.log.debug('getCmdMac', this.mac)
            $log.writeBody('getCmdMac', this.mac)
            console.log('getCmdMac', getCmdMac(cmd))
          }

          //const sendCmd = this.buildCmd(0x13, scaleType, 0x01, 0x10, 0x00, 0x00, 0x00);

          //安卓平台上，在调用 notifyBLECharacteristicValueChange 成功后立即调用 writeBLECharacteristicValue 接口，在部分机型上会发生 10008 系统错误          
          if (this.cmdTimer) return
          this.cmdTimer = setTimeout(() => {
            const sendCmd = this.buildCmd(0x13, scaleType, 0x01, 0x10, 0x00, 0x00, 0x00);
            this.writeData(sendCmd)
            this.cmdTimer = false
          }, this.listener.delay_notification || 1)
          break;
        }
      case 0x10:
        {
          const weight = decodeWeight(data[3], data[4]);
          let resistance = this.decodeImpedance(data[6], data[7])
          let resistance500 = this.decodeImpedance(data[8], data[9])
          let heart_rate = 0

          if (this.heartRate) {
            heart_rate = data[10] == 0 ? 0 : +("0x" + arrayToHexString([data[10]]))
          }

          //let manufacturer = arrayToHexString([data[2]]) + arrayToHexString([data[3]])
          let manufacturer = this.manufacturer
        
          const scaleParam = {
            mac: (this.device && this.device.mac) || this.mac,
            scale_weight: weight,
            scale_resistance: resistance,
            scale_resistance500: resistance500,
            heart_rate,
            manufacturer,
            app_type: appType,
          }
          if (scaleType == 0xff) {//心率双模秤
            let left_weight = decodeWeight(data[11], data[12])

            Object.assign(scaleParam, {
              left_weight,
              right_weight: this.accuracy(weight, left_weight)
            })
          }
          if (data[5] === 0) {
            this.lastWeightTime = weight == 0 ? 0 : new Date().getTime()
            this.stableDataStatus = false
            this.listener.onWeight && this.listener.onWeight(weight)
          } else if (data[5] === 1) {
            const scaleString = arrayToHexString(data) + this.cmdString

            this.listener && this.listener.onStartFetch(weight)

            //if (this.ishearRate(scaleType)) {
            if (this.heartRate) {
              if (resistance != 0 && resistance500 != 0) {
                scaleParam.device_type = 1
              }
              //心率秤，计算指标
              this.listener.onSteadyData && this.listener.onSteadyData({ scaleParam, returnStatus: true })
              .then(measure => {
                if (measure) {
                  const {
                    bodyfat,
                    bmi
                  } = measure
                  const bodyfatItem = BodyfatReport.build(measure)
                  const bmiItem = BmiReport.build(measure)

                  const bodyfatLevel = bodyfatItem ? bodyfatItem.level + 1 : 1;
                  const bmiLevel = bmiItem ? bmiItem.level + 1 : 1;

                  const _bodyfat1 = (bodyfat * 10) >> 8
                  const _bodyfat2 = (bodyfat * 10) & 0xFF

                  const _bmi1 = (bmi * 10) >> 8
                  const _bmi2 = (bmi * 10) & 0xFF

                  const sendCmd = this.buildCmd(0x1f, scaleType, 0x10, _bodyfat1, _bodyfat2, bodyfatLevel, _bmi1, _bmi2, bmiLevel)
                  this.writeData(sendCmd)
                }
              })
            } else {
              //非心率秤，下发结束命令
              const sendCmd = this.buildCmd(0x1f, scaleType, 0x10);
              this.writeData(sendCmd)

              const testTime = new Date().getTime() - this.lastWeightTime
              const hasResistance = testTime > 4000
              //在这判断是否需要
              console.log("测量阻抗时间", testTime)
              if (this.stableDataStatus) {
                console.log("stableDataStatus 为true，不再处理", this.stableDataStatus)
                return
              }
              this.stableDataStatus = true
              this.listener.onStartRequest && this.listener.onStartRequest(weight)
              this.listener.onSteadyData && this.listener.onSteadyData({ scaleParam, impedance: this.impedance(this.scaleData) })
            }
          } else if (data[5] === 2) {
            const sendCmd = this.buildCmd(0x1f, scaleType, 0x10);
            this.writeData(sendCmd)

            const scaleString = arrayToHexString(data) + this.cmdString
   
            if (scaleParam.device_type) {
              delete scaleParam.device_type
            }
            if (this.stableDataStatus) return
            this.stableDataStatus = true
            this.listener.onStartRequest && this.listener.onStartRequest(weight)
            this.listener.onSteadyData && this.listener.onSteadyData({ scaleParam, impedance: this.impedance(this.scaleData) })
          }
          break;
        }
      case 0x14:
        {
          if (!this.isScale1) {
            return
          }
          const differ = moment().unix() - SECOND_2000_YEAR;
          var timeData = [];
          for (var i = 0; i < 4; i++) {
            timeData.push(differ >> (i * 8));
          }
          this.writeData(this.buildCmd(0x20, scaleType, timeData[0], timeData[1], timeData[2], timeData[3]))
          break;
        }
      default:
        if (this.nowUUID == UUID_WSP_2A9D) {
          this.listener.onWspMeasure && this.listener.onWspMeasure(data)
        }
    }
  }

  accuracy(v1, v2) {//为防止小数计算，精度丢失
    let n = 10000

    return (v1 * n - v2 * n) / n
  }
  
  ishearRate(scaleType, _bit) {
    //return scaleType === 0x21 || scaleType === 0xff
    if (scaleType === 0x21) {
      return true
    } else if (scaleType === 0xff) {
      console.log("_bit", _bit)
      return (_bit >> 5) & 1
    }
    return false
  }
  
  isHeartRateScale(scaleType) {
    return scaleType === 0x21
  }
  
  decodeImpedance(a1, b1) {
    return (a1 << 8) + b1
  }

  formatCmdData(cmd) {
    let length = cmd.length
    let index = 0
    let len = 2
    let tmp = []

    while (index < length) {
      tmp.push(cmd.substr(index, len))
      index += len
    }

    return tmp
  }

  impedanceBroadcast(impedance = 0, impedance2 = 0) {
    let _impedance = 0, _impedance2 = 0

    _impedance = impedance * 2 + impedance2 * 3
    _impedance2 = impedance * 3 - impedance2 * 2
  
    return {
      impedance50: _impedance,
      impedance500: _impedance2
    }
  }

  impedance(scaleData) {
    let _impedance = 0, _impedance2 = 0

    if (this.isType(scaleData, "Array") && scaleData.length) {
      let impedance = `0x${scaleData[6]}${scaleData[7]}`
      let impedance2 = `0x${scaleData[8]}${scaleData[9]}`

      _impedance = impedance * 2 + impedance2 * 3
      _impedance2 = impedance * 3 - impedance2 * 2
    }

    return {
      impedance50: _impedance,
      impedance500: _impedance2
    }
  }

  isType(arg, type) {
    return Object.prototype.toString.call(arg) == `[object ${type}]`
  }

  outRequestEnd(measure) {
    const {
      bodyfat,
      bmi
    } = measure
    const bodyfatItem = BodyfatReport.build(measure)
    const bmiItem = BmiReport.build(measure)

    const bodyfatLevel = bodyfatItem ? bodyfatItem.level + 1 : 1;
    const bmiLevel = bmiItem ? bmiItem.level + 1 : 1;

    const _bodyfat1 = (bodyfat * 10) >> 8
    const _bodyfat2 = (bodyfat * 10) & 0xFF

    const _bmi1 = (bmi * 10) >> 8
    const _bmi2 = (bmi * 10) & 0xFF

    const sendCmd = this.buildCmd(0x1f, this.scaleType, 0x10, _bodyfat1, _bodyfat2, bodyfatLevel, _bmi1, _bmi2, bmiLevel)
    this.writeData(sendCmd)
  }
}

module.exports = Scale